//
// Created by mio on 25-4-17.
//

#ifndef RTMPCHUNK_H
#define RTMPCHUNK_H

#include <map>

#include "../Net/BufferReader.h"
#include "../Net/BufferWriter.h"
#include "RtmpMessage.h"


/**
 * @class RtmpChunk
 * @brief 处理 RTMP Chunk 的解析（解包）与创建（封包）。
 *
 * 该类负责根据 RTMP 协议规范，将接收到的字节流解析成一个个 RTMP Chunk，
 * 并根据 Chunk Header 的信息（特别是 Chunk Stream ID, CSID）将属于同一个
 * RTMP Message 的 Chunk 数据组装起来，最终还原成一个完整的 RtmpMessage。
 *
 * 同时，它也提供了将一个完整的 RtmpMessage 按照指定的 CSID 和
 * out_chunk_size_ 分割并打包成符合 RTMP 规范的一个或多个 Chunk 的功能，
 * 以便发送出去。
 */
class RtmpChunk
{
public:
    enum State
    {
        PARSE_HEADER,
        PARSE_BODY
    };
    RtmpChunk();
    virtual ~RtmpChunk();
    /**
     * @brief 从输入的 BufferReader 中解析 RTMP 块数据。
     *        如果解析出一个或多个块后，能成功组装成一个完整的 RTMP 消息，
     *        则该消息会被填充到 out_rtmp_msg 中。
     * @param in_buffer 包含待解析数据的输入缓冲区。
     * @param out_rtmp_msg 用于接收解析完成的完整 RTMP 消息的输出参数。
     * @return int > 0: 成功解析并消耗的字节数。
     *             0: 数据不足，需要更多数据才能继续解析。
     *             < 0: 解析过程中发生错误。
     *        注意：一次调用可能只解析了一个块的一部分、一个完整的块或多个块。
     *             只有当一个完整的 RTMP 消息被组装完毕时，out_rtmp_msg 才会被填充。
     */
    int Parse(BufferReader& in_buffer, RtmpMessage& out_rtmp_msg);
    /**
     * @brief 将一个完整的 RTMP 消息 (in_msg) 按照指定的 CSID 拆分成 RTMP 块，
     *        并将序列化后的块数据写入到提供的缓冲区 (buf) 中。
     * @param csid 要使用的 Chunk Stream ID。
     * @param in_msg 待拆分和发送的完整 RTMP 消息。
     * @param buf 用于存储生成的块数据的输出缓冲区。
     * @param buf_size 输出缓冲区的大小。
     * @return int > 0: 成功写入到 buf 中的总字节数。
     *             -1: 输出缓冲区大小不足或发生错误。
     */
    int CreateChunk(uint32_t csid, RtmpMessage& in_msg, char* buf, uint32_t buf_size);
    /**
     * @brief 设置接收块的最大尺寸。影响 ParseChunkBody 如何读取数据。
     * @param in_chunk_size 新的输入块大小。
     */
    void SetInChunkSize(uint32_t in_chunk_size) { in_chunk_size_ = in_chunk_size; }
    /**
     * @brief 设置发送块的最大尺寸。影响 CreateChunk 如何拆分消息。
     * @param out_chunk_size 新的输出块大小。
     */
    void SetOutChunkSize(uint32_t out_chunk_size) { out_chunk_size_ = out_chunk_size; }
    void Clear() { rtmp_message_.clear(); }
    int GetStreamId() const { return streamid_; }

protected:
    /**
     * @brief 解析 RTMP 块头（Basic Header 和 Message Header）。
     * @param buffer 输入缓冲区。
     * @return int > 0: 成功解析并消耗的字节数。
     *             0: 数据不足。
     *             < 0: 解析错误。
     */
    int ParseChunkHeader(BufferReader& buffer);
    /**
     * @brief 解析 RTMP 块体（Payload）。
     * @param buffer 输入缓冲区。
     * @return int > 0: 成功解析并消耗的字节数。
     *             0: 数据不足。
     *             < 0: 解析错误。
     */
    int ParseChunkBody(BufferReader& buffer);
    /**
     * @brief 创建 RTMP 块的基础头（Basic Header）。
     * @param fmt 块类型 (0-3)。
     * @param csid Chunk Stream ID。
     * @param buf 输出缓冲区指针。
     * @return int 创建的 Basic Header 的字节数 (1, 2, 或 3)。
     */
    int CreateBasicHeader(uint8_t fmt, uint32_t csid, char* buf);
    /**
     * @brief 创建 RTMP 块的消息头（Message Header）。
     * @param fmt 块类型 (0-3)，决定包含哪些字段。
     * @param rtmp_msg 包含要编码的头信息的 RtmpMessage 对象。
     * @param buf 输出缓冲区指针。
     * @return int 创建的 Message Header 的字节数 (0, 3, 7, 或 11)。
     */
    int CreateMessageHeader(uint8_t fmt, RtmpMessage& rtmp_msg, char* buf);

private:
    State state_;                               // 当前解析状态
    int chunk_stream_id_ = 0;                   // 正在组装的 CSID
    static int streamid_;                       // 全局递增的“类实例序号”，便于调试
    uint32_t in_chunk_size_ = 128;              // 对端发来的 chunk size（缺省 128）
    uint32_t out_chunk_size_ = 128;             // 本端发送的 chunk size
    /**
     * @brief 存储部分接收到的 RTMP 消息。
     *        键 (int): Chunk Stream ID (CSID)。
     *        值 (RtmpMessage): 对应 CSID 上正在组装的 RTMP 消息的状态。
     *                       包含已接收的负载、总长度、时间戳、类型、流ID等信息。
     */
    std::map<int, RtmpMessage> rtmp_message_;
    /**
     * @brief 预定义的常量数组，存储不同块类型 (fmt 0-3) 对应的消息头长度。
     *        fmt=0: 11 bytes (timestamp + length + type_id + stream_id)
     *        fmt=1: 7 bytes (timestamp + length + type_id)
     *        fmt=2: 3 bytes (timestamp)
     *        fmt=3: 0 bytes (无消息头)
     */
    const int KChunkMessageHeaderLength[4] = {11, 7, 3, 0};
};


#endif //RTMPCHUNK_H
